/**
 * Copyright (c) 2008, EffCode, www.effcode.com
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list 
 * of conditions and the following disclaimer. 
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution. 
 * Neither the name of the EffCode nor the names of its contributors may be used 
 * to endorse or promote products derived from this software without specific prior written 
 * permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.effdom;

/**
 * The <code>Element</code> is the data container in the EffDom concept. An
 * element can contain attributes and other elements. The data structure is
 * similar to XML. Each attribute must have an, within the element's attribute
 * context, unique identifier - the attribute ID. The ID is used to retrieve
 * wanted attribute from the element. <p/> An element can have zero or several
 * child elements in the same way as in XML. The elements are identfied by the
 * element ID. <p/> Please see the API documentation for the
 * <code>{@link Document}</code> class for how-to use element.
 * 
 * @author <a href="mailto:mattias@effcode.com">Mattias Jonsson</a>
 * @see Document
 * @see Attr
 */
public interface Element extends Node {

    /**
     * Returns <code>true</code> if the element contains any attribute.
     * 
     * @return <code>true</code> if element has any attributes, else
     *         <code>false</code>.
     */
    public boolean hasAttr();

    /**
     * Returns <code>true</code> if the element contains the specified
     * attribute.
     * 
     * @return <code>true</code> if element has the specified attributes, else
     *         <code>false</code>.
     */
    public boolean hasAttr(short id);

    /**
     * Returns the element's attributes represented as an <code>Iterable</code>.
     * <p/> The attributes are ordered in the same way they where added.
     * 
     * @return the element's attributes.
     */
    public Iterable<Attr> attrs();

    /**
     * Returns the attribute with the specified ID or null if the element
     * doesn't have an attribute with given ID.
     * 
     * @param id
     *            the atribute ID.
     * @return the attribute with given ID or null.
     */
    public Attr getAttr(short id);

    /**
     * Returns the specified attribute's value represented as an string.
     * Primitives like int and long are converted to a string using
     * <code>String.valueOf method</code>. If the attribute is a binary
     * value, a string is created using the binary value as argument to
     * constructor, new String(bytes).
     * 
     * @return the attribute value represented as a <code>String</code> or
     *         null if the <code>Attr</code> doesn't contain any data.
     * 
     * @throws IllegalStateException
     *             an <code>IllegalStateException</code> is thrown if the
     *             <code>Attr</code> is unable to return the <code>Attr</code>
     *             value.
     */
    public String attrValue(short id) throws IllegalStateException;

    /**
     * Returns the specified attribute's value as a byte array. The method is
     * typically used for binary data. It is not supposed to use for any other
     * kind of data. If the attribute doesn't support to return the current hold
     * data represented as a byte array an <code>IllegalStateException</code>
     * will be raised.
     * 
     * @return the attribute value as a a byte array or null if the attribute
     *         doesn't contain any data.
     * 
     * @throws IllegalStateException
     *             an <code>IllegalStateException</code> is thrown if the
     *             <code>Attr</code> is unable to return the <code>Attr</code>
     *             value. This may happen if the <code>Attr</code>
     *             implementation can't return the current hold data as a byte
     *             array.
     */
    public byte[] attrValueAsByteArray(short id) throws IllegalStateException;

    /**
     * Returns the specified attribute's value as a <code>int</code>. If the
     * attribute contains an number greater than Integer.MAX or less than
     * Integer.MIN or if the value is not a number an
     * <code>IllegalStateException</code> will be raised.
     * 
     * @return the attribute value as an <code>int</code>. Default value is
     *         0.
     * 
     * @throws IllegalStateException
     *             an <code>IllegalStateException</code> is thrown if the
     *             <code>Attr</code> is unable to return the <code>Attr</code>
     *             value. This may happen if the <code>Attr</code>
     *             implementation can't return the current hold data as an
     *             <code>int</code>.
     */
    public int attrValueAsInt(short id) throws IllegalStateException;

    /**
     * Returns the specified attribute's value as a <code>long</code>. If the
     * <code>Attr</code> contains an value that is not a number an
     * <code>IllegalStateException</code> will be raised.
     * 
     * @return the attribute value as an <code>long</code>. Default value is
     *         0.
     * 
     * @throws IllegalStateException
     *             an <code>IllegalStateException</code> is thrown if the
     *             <code>Attr</code> is unable to return the <code>Attr</code>
     *             value. This may happen if the <code>Attr</code>
     *             implementation can't return the current hold data as an
     *             <code>long</code>.
     */
    public long attrValueAsLong(short id) throws IllegalStateException;

    /**
     * Creates an attrbute with data type <code>String</code> and adds it to
     * the element. <p/> The element is returned to make it easy to call the
     * <code>attr()</code> in a chain like: <p/>
     * 
     * <code>elem.attr(1, "Hello").attr(2, "World");</code>
     * 
     * @param id
     *            the attribute ID
     * @param value
     *            the attribute value
     * @return the element to which the attribute is added
     * @throws IllegalStateException
     */
    public Element attr(short id, String value) throws IllegalStateException;

    /**
     * Creates an attrbute with data type <code>byte array</code> and adds it
     * to the element. Use this method for adding binary data like images, files
     * etc.<p/> The element is returned to make it easy to call the
     * <code>attr()</code> in a chain like: <p/>
     * 
     * <code>elem.attr(1, "Hello").attr(2, "World");</code>
     * 
     * @param id
     *            the attribute ID
     * @param value
     *            the attribute value
     * @return the element to which the attribute is added
     * @throws IllegalStateException
     */
    public Element attr(short id, byte[] value) throws IllegalStateException;

    /**
     * Creates an attrbute with data type <code>int</code> and adds it to the
     * element. <p/> The element is returned to make it easy to call the
     * <code>attr()</code> in a chain like: <p/>
     * 
     * <code>elem.attr(1, "Hello").attr(2, "World");</code>
     * 
     * @param id
     *            the attribute ID
     * @param value
     *            the attribute value
     * @return the element to which the attribute is added
     * @throws IllegalStateException
     */
    public Element attr(short id, int value) throws IllegalStateException;

    /**
     * Creates an attrbute with data type <code>long</code> and adds it to the
     * element. <p/> The element is returned to make it easy to call the
     * <code>attr()</code> in a chain like: <p/>
     * 
     * <code>elem.attr(1, "Hello").attr(2, "World");</code>
     * 
     * @param id
     *            the attribute ID
     * @param value
     *            the attribute value
     * @return the element to which the attribute is added
     * @throws IllegalStateException
     */
    public Element attr(short id, long value) throws IllegalStateException;

    /**
     * Removes the specified attribute.
     * 
     * @param id
     *            the attribute ID.
     * @return the removed attribute or null
     */
    public Attr removeAttr(short id);

    /**
     * Returns <code>true</code> if the element has any child elements.
     * 
     * @return Returns <code>true</code> if the element has any child
     *         elements, otherwise <code>false</code> is returned
     */
    public boolean hasElements();

    /**
     * Returns number of child elements with the specified element ID.
     * 
     * @return Returns the count of child element with the specified ID.
     */
    public int hasElements(short id);

    /**
     * Returns an <code>Iterable</code> with all child elements with the
     * specified ID. The elements are ordered in the same way they where added.
     * 
     * @param id
     *            the child element ID
     * @return the child elements or empty
     */
    public Iterable<Element> elements(short id);

    /**
     * Returns an <code>Iterable</code> with all child elements. The elements
     * are ordered in the same way they where added.
     * 
     * @return the child elements or empty
     */
    public Iterable<Element> elements();

    /**
     * Returns the first child element with the specified ID or null.
     * 
     * @param id
     *            the child element ID
     * @return the first child
     */
    public Element firstElement(short id);

    /**
     * Returns the first child element or null.
     * 
     * @return the first child
     */
    public Element firstElement();

    /**
     * Creates an <code>Element</code> with the specified ID and appends it to
     * the current element. The created <code>Element</code> is returned.
     * 
     * @param id
     *            the element ID
     * @return the newly created and appeded element
     * @throws IllegalStateException
     */
    public Element element(short id) throws IllegalStateException;

    /**
     * Appends specified <code>Element</code> to
     * the current element. The parent <code>Element</code> is returned.
     * 
     * @param id
     *            the element ID
     * @return the parent (current) element
     * @throws IllegalStateException
     */
    public Element element(Element elem) throws IllegalStateException;

    /**
     * Removes the specified element.
     * 
     * @param element
     *            the element to be removed
     * @return the removed element
     * @throws IllegalStateException
     */
    public Element removeElement(Element element) throws IllegalStateException;

}
